#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <memory.h>
#include <bios.h>

/* Eliminate unnecessary code in the EXE image... */
char **__crt0_glob_function(char *arg) { return 0; }
void __crt0_load_environment_file(char *progname) {}

#ifndef	FALSE
#define	FALSE	0
#endif
#ifndef	TRUE
#define	TRUE	1
#endif

#define	SECTOR_SIZE	512

typedef	int		BOOL ;
typedef unsigned char	BYTE8 ;
typedef unsigned short	WORD16 ;
typedef unsigned long	DWORD32 ;

#define	PCK	__attribute__((packed))

typedef struct PARAMS
	{
	WORD16	bytes_per_sctr	PCK ;	/* Bytes per sector		*/
	BYTE8	sctrs_per_clust PCK ;	/* Sectors per cluster		*/
	WORD16	rsvd_sectors	PCK ;	/* rsvd sectors (boot sector)	*/
	BYTE8	number_fats	PCK ;	/* Number of FATs		*/
	WORD16	dir_entries	PCK ;	/* # root-directory entries	*/
	WORD16	total_sctrs	PCK ;	/* # sectors in logical volume	*/
	BYTE8	media_desc	PCK ;	/* Media descriptor byte	*/
	WORD16	sctrs_per_fat	PCK ;	/* Sectors per FAT		*/

	/* Additional information (MS/DOS 3.0) ...			*/

	WORD16	sctrs_per_track	PCK ;	/* Sectors per track		*/
	WORD16	number_heads	PCK ;	/* Number of heads		*/
	DWORD32	hidden_sctrs	PCK ;	/* Number of hidden sectors	*/

	/* Additional information (MS/DOS 4.0) ...			*/

	DWORD32	ttl_sctrs_vol	PCK ;	/* ttl log sctrs in log volume	*/
	BYTE8	phys_drive	PCK ;	/* physical drive number	*/
	BYTE8	reserved1	PCK ;	/* reserved			*/
	BYTE8	ext_boot_sig	PCK ;	/* extended boot signature	*/
	DWORD32	volume_id	PCK ;	/* 32-bit binary volume ID	*/
	BYTE8	volume_label[11] PCK ;	/* volume label			*/
	BYTE8	reserved2[8]	PCK ;	/* reserved			*/

	/* Additional information written by COPYBOOT.EXE ...		*/

	WORD16	fat_nibbles	PCK ;	/* 4-bit Nibbles per FAT entry	*/
	WORD16	bytes_per_clust	PCK ;	/* Bytes per cluster		*/
	WORD16	dir_sector0	PCK ;	/* log sctr # of 1st dir sctr	*/
	WORD16	data_sector0	PCK ;	/* log sctr # of 1st data sctr	*/
	WORD16	dir_sectors	PCK ;	/* # directory sectors		*/
	WORD16	para_per_clust	PCK ;	/* paragraphs per cluster	*/
	WORD16	kb_needed	PCK ;	/* total memory space required	*/
	WORD16	end_of_chain	PCK ;	/* end marker for FAT chain	*/
	} PARAMS ;

void	Abort(char *fmt, ...) ;
BOOL	FAT12(int drive) ;
BOOL	FAT16(int drive) ;
BYTE8 *	GetFAT(int drive) ;
BOOL	Read_Sector(int drive, int sector, void *buffer) ;
BOOL	RW_Sector(int command, int drive, int sector, void *buffer) ;
void	Summary(PARAMS *p) ;
void	Usage(void) ;
BOOL	Write_Sector(int drive, int sector, void *buffer) ;

BOOL	summary = FALSE ;

int main(int argc, char *argv[])
	{
	static char filename[] = "BOOTLOAD.BIN" ;
	int target_drive, sctr_buffers, bytes_needed ;
	static char old[SECTOR_SIZE] ;
	static char new[SECTOR_SIZE] ;
	BOOL fat12, fat16 ;
	char *diskname ;
	PARAMS *p ;
	FILE *fp ;

	if (argc != 2) Usage() ;

	strupr(diskname = argv[1]) ;

	if (strcmp(diskname, "A") && strcmp(diskname, "A:") &&
	    strcmp(diskname, "B") && strcmp(diskname, "B:"))
	    	{
		Usage() ;
		}

	target_drive = diskname[0] - 'A' ;

	fat12 = FAT12(target_drive) ;
	fat16 = FAT16(target_drive) ;
	if (!fat12 && !fat16)
		{
		Abort("Target diskette is NOT formatted FAT12 or FAT16!\n") ;
		}

	fp = fopen(filename, "rb") ;
	if (!fp) Abort("Can't open file: %s\n", filename) ;
	if (fread(new, sizeof(new), 1, fp) != 1)
		{
		Abort("Can't read file: %s\n", filename) ;
		}
	fclose(fp) ;

	if (!Read_Sector(target_drive, 0, old))
		{
		Abort("Can't read boot sector of drive %c.\n",
			diskname[0]) ;
		}

	memcpy(p = (PARAMS *) &new[11], &old[11], sizeof(PARAMS)) ;

	p->ext_boot_sig = 0x29 ;

	if (fat12) p->fat_nibbles = 3 ;
	if (fat16) p->fat_nibbles = 4 ;
	p->bytes_per_clust = p->bytes_per_sctr * p->sctrs_per_clust ;
	p->dir_sector0     = 1 + p->number_fats * p->sctrs_per_fat ;
	p->data_sector0    = p->dir_sector0 +
				(32 * p->dir_entries) / p->bytes_per_sctr ;
	p->dir_sectors     = (32 * p->dir_entries) / p->bytes_per_sctr ;
	p->para_per_clust  = p->bytes_per_clust / 16 ;

	sctr_buffers       = p->dir_sectors ;
	if (p->sctrs_per_fat > sctr_buffers) sctr_buffers = p->sctrs_per_fat ;
	bytes_needed       = p->bytes_per_sctr * (1 + sctr_buffers) ;
	p->kb_needed       = (bytes_needed + 1023) / 1024 ;
	p->end_of_chain    = fat16 ? 0xFFF0 : 0x0FF0 ;

	Summary(p) ;

	if (!Write_Sector(target_drive, 0, new))
		{
		Abort("Can't write boot sector of drive %c.\n",
			diskname[0]) ;
		}

	printf("Boot loader transferred to boot sector of drive '%c'.\n",
		diskname[0]) ;
	return 0 ;
	}

void Summary(PARAMS *p)
	{
	char label[12] ;

	if (!summary) return ;

	printf("\n") ;

	printf("%11d Bytes per sector\n", p->bytes_per_sctr) ;
	printf("%11d Sectors per cluster\n", p->sctrs_per_clust) ;
	printf("%11d rsvd sectors (boot sector)\n", p->rsvd_sectors) ;
	printf("%11d Number of FATs\n", p->number_fats) ;
	printf("%11d root-directory entries\n", p->dir_entries) ;
	printf("%11d sectors in logical volume\n", p->total_sctrs) ;
	printf("%11X Media descriptor byte\n", p->media_desc) ;
	printf("%11d Sectors per FAT\n", p->sctrs_per_fat) ;

	/* Additional information (MS/DOS 3.0) ...			*/

	printf("%11d Sectors per track\n", p->sctrs_per_track) ;
	printf("%11d Number of heads\n", p->number_heads) ;
	printf("%11ld Number of hidden sectors\n", p->hidden_sctrs) ;

	/* Additional information (MS/DOS 4.0) ...			*/

	printf("%11ld ttl log sctrs in log volume\n", p->ttl_sctrs_vol) ;
	printf("%11d physical drive number\n", p->phys_drive) ;
	printf("%11X reserved\n", p->reserved1) ;
	printf("%11X extended boot signature\n", p->ext_boot_sig) ;
	printf("%11lX 32-bit binary volume ID\n", p->volume_id) ;
	memcpy(label, p->volume_label, 11) ;
	label[11] = '\0' ;
	printf("%s volume label\n", label) ;

	/* Additional information written by COPYBOOT.EXE ...		*/

	printf("%11d 4-bit Nibbles per FAT entry\n", p->fat_nibbles) ;
	printf("%11d Bytes per cluster\n", p->bytes_per_clust) ;
	printf("%11d log sctr # of 1st dir sctr\n", p->dir_sector0) ;
	printf("%11d log sctr # of 1st data sctr\n", p->data_sector0) ;
	printf("%11d directory sectors\n", p->dir_sectors) ;
	printf("%11d paragraphs per cluster\n", p->para_per_clust) ;
	printf("%11d total KB memory space required\n", p->kb_needed) ;
	printf("%11X end marker for FAT chain\n", p->end_of_chain) ;
	printf("\n") ;
	}

BYTE8 *GetFAT(int drive)
	{
	static BYTE8 fat[SECTOR_SIZE] ;
	static int previous = -1 ;

	if (drive != previous)
		{
		if (!Read_Sector(drive, 1, fat))
			{
			Abort("Can't read 1st sector of 1st FAT!") ;
			}
		previous = drive ;
		}

	return fat ;
	}

BOOL FAT12(int drive)
	{
	BYTE8 *fat = GetFAT(drive) ;
	return fat[1] == 0xFF && fat[2] == 0xFF && fat[3] != 0xFF ;
	}

BOOL FAT16(int drive)
	{
	BYTE8 *fat = GetFAT(drive) ;
	return	fat[1] == 0xFF && fat[2] == 0xFF &&
		fat[3] == 0xFF && fat[4] != 0xFF ;
	}

BOOL RW_Sector(int command, int drive, int sector, void *buffer)
	{
	int attempt ;

	sector++ ;
	for (attempt = 0; attempt < 3; attempt++)
		{
		if (!biosdisk(command, drive, 0, 0, sector, 1, buffer))
			{
			return TRUE ;
			}

		biosdisk(0, drive, 0, 0, 0, 0, NULL) ;
		}

	return FALSE ;
	}

BOOL Read_Sector(int drive, int sector, void *buffer)
	{
	return RW_Sector(2, drive, sector, buffer) ;
	}

BOOL Write_Sector(int drive, int sector, void *buffer)
	{
	return RW_Sector(3, drive, sector, buffer) ;
	}

void Abort(char *fmt, ...)
	{
	va_list args ;
	va_start(args, fmt) ;
	vfprintf(stderr, fmt, args) ;
	va_end(args) ;
	exit(255) ;
	}

void Usage(void)
	{
	Abort("Usage: COPYBOOT A:\n") ;
	}
